/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'dart:async';

import 'package:flatterer/flatterer.dart';
import 'package:flatterer/src/change_notifier.dart';
import 'package:flatterer/src/dimens.dart';
import 'package:flatterer/src/geometry.dart';
import 'package:flatterer/src/indicate_border.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

/// Created by changlei on 2020/8/5.
///
/// overlay浮层锚点控件
class OverlayWindowAnchor extends StatefulWidget {
  /// 浮动提示
  const OverlayWindowAnchor({
    Key? key,
    required this.child,
    required this.builder,
    this.offset = 0,
    this.indicateSize = defaultIndicateSize,
    this.direction = Axis.vertical,
    this.margin = defaultMargin,
    this.alignment = 0,
    this.link,
    this.onDismiss,
    this.backgroundColor = Colors.white,
    this.borderRadius = defaultBorderRadius,
    this.shadows = defaultShadows,
    this.side = defaultSide,
    this.barrierDismissible = true,
    this.barrierColor,
    this.preferBelow = true,
    this.rootOverlay = true,
    this.below,
    this.above,
    this.modalBarrier = false,
    this.showWhenUnlinked = false,
    this.useSafeArea = false,
  }) : super(key: key);

  /// 需要对齐的child
  final Widget child;

  /// 构建弹窗内容
  final WidgetBuilder builder;

  /// 水平或垂直方向上的偏移量
  final double offset;

  /// 显示的方向，上下结构和左右结构
  final Axis direction;

  /// 三角指示器的大小
  final RSize indicateSize;

  /// 距离屏幕或者[parentRegion]的距离
  final EdgeInsetsGeometry margin;

  /// 对齐方式，[-1,1]，0为居中，-1为最左边，1为最右边
  final double alignment;

  /// 跟踪者
  final LayerLink? link;

  /// 隐藏回调
  final VoidCallback? onDismiss;

  /// 窗口背景颜色
  final Color backgroundColor;

  /// 矩形框的圆角
  final BorderRadiusGeometry borderRadius;

  /// 阴影
  final List<BoxShadow> shadows;

  /// The border outline's color and weight.
  ///
  /// If [side] is [BorderSide.none], which is the default, an outline is not drawn.
  /// Otherwise the outline is centered over the shape's boundary.
  final BorderSide side;

  /// 点击外部区域弹窗是否消失
  final bool barrierDismissible;

  /// 遮罩颜色
  final Color? barrierColor;

  /// 优先显示在末尾
  final bool preferBelow;

  /// 使用跟路由
  final bool rootOverlay;

  /// 显示在below
  final ValueGetter<OverlayWindowAnchorState?>? below;

  /// 显示在above
  final ValueGetter<OverlayWindowAnchorState?>? above;

  /// 是否使用[ModalBarrier]
  final bool modalBarrier;

  /// [CompositedTransformFollower.showWhenUnlinked]
  final bool showWhenUnlinked;

  /// [showDialog.useSafeArea]
  final bool useSafeArea;

  @override
  OverlayWindowAnchorState createState() => OverlayWindowAnchorState();
}

/// 浮动提示state
class OverlayWindowAnchorState extends State<OverlayWindowAnchor> with SingleTickerProviderStateMixin {
  final _hitTestDetector = HitTestDetector();

  late LayerLink _link;
  late AnimationController _controller;

  late OverlayWindow _overlayWindow;
  OverlayWindow? _previousOverlayWindow;

  Rect? _anchor;
  VoidCallback? _listener;
  bool _isTapDownHit = false;

  @override
  void initState() {
    _createOverlayWindow();
    _controller = AnimationController(
      vsync: this,
      duration: fadeDuration,
    );
    _link = widget.link ?? LayerLink();
    _hitTestDetector.setup(
      onPointerEvent: _handlePointerEvent,
    );
    super.initState();
  }

  @override
  void didUpdateWidget(covariant OverlayWindowAnchor oldWidget) {
    if (widget.link != oldWidget.link) {
      _link = widget.link ?? (oldWidget.link == null ? _link : LayerLink());
    }
    if (widget.rootOverlay != oldWidget.rootOverlay) {
      _previousOverlayWindow = _overlayWindow;
      if (_overlayWindow.mounted) {
        dismiss();
      } else {
        _disposePreviousOverlayWindow();
      }
      _createOverlayWindow();
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    _overlayWindow.removeListener(_onDismissed);
    _overlayWindow.dismiss(immediately: true);
    _overlayWindow.dispose();
    _disposePreviousOverlayWindow();
    _controller.dispose();
    _hitTestDetector.dispose();
    super.dispose();
  }

  void _createOverlayWindow() {
    _overlayWindow = OverlayWindow(
      context,
      rootOverlay: widget.rootOverlay,
    );
    _overlayWindow.addListener(_onDismissed);
  }

  void _disposePreviousOverlayWindow() {
    final overlayWindow = _previousOverlayWindow;
    _previousOverlayWindow = null;
    if (overlayWindow == null) {
      return;
    }
    overlayWindow.removeListener(_onDismissed);
    overlayWindow.dismiss(immediately: true);
    overlayWindow.dispose();
  }

  /// 针对[PointerEvent]做命中测试
  ///
  /// 返回是否命中
  bool hitTest(PointerEvent event, [HitTestResultVisitor? other]) {
    bool visitor(HitTestTarget target, Object? data) {
      return other?.call(target, data) == true || this.visitor(target, data);
    }

    if (event is PointerDownEvent) {
      _isTapDownHit = event.result.any(visitor);
    } else if (event is PointerUpEvent && !_isTapDownHit && !event.result.any(visitor)) {
      _isTapDownHit = false;
      return true;
    }
    return false;
  }

  /// 测试是否需要dismiss
  bool visitor(HitTestTarget target, Object? data) {
    return data == this || data == widget.child || target == context.findRenderObject();
  }

  void _handlePointerEvent(PointerEvent event) {
    if (!isShowing || !widget.barrierDismissible || widget.modalBarrier) {
      return;
    }
    if (hitTest(event)) {
      dismiss();
    }
  }

  /// 是否正在显示
  bool get isShowing => _overlayWindow.mounted;

  /// 显示
  ///
  /// [anchor]-锚点，在屏幕中的位置
  /// [bounds]-边界，在屏幕中的位置
  /// [compositedTransformTarget]-compositedTransformTarget控件的坐标，默认为anchor的坐标
  void show({
    Rect? anchor,
    Rect? compositedTransformTarget,
    Rect? bounds,
    TrackBehavior behavior = TrackBehavior.lazy,
  }) {
    _disposePreviousOverlayWindow();
    final rectTween = RectTween(begin: _anchor, end: anchor);
    final animation = rectTween.animate(_controller);
    void animationListener() {
      if (animation.isCompleted) {
        _listener = null;
        animation.removeListener(animationListener);
      }

      final value = animation.value;
      if (behavior == TrackBehavior.lazy) {
        _anchor = value;
      }
      _showOrUpdate(value, compositedTransformTarget, bounds: bounds);
    }

    final listener = _listener;
    _listener = null;
    if (listener != null) {
      animation.removeListener(listener);
    }
    animation.addListener(_listener = animationListener);

    if (anchor != null && _anchor != null && anchor != _anchor && behavior != TrackBehavior.none) {
      _controller.forward(from: _controller.lowerBound);
    } else {
      _controller.value = _controller.upperBound;
    }
    if (behavior == TrackBehavior.sharp) {
      _anchor = anchor;
    }
  }

  void _showOrUpdate(Rect? anchor, Rect? compositedTransformTarget, {Rect? bounds}) {
    _overlayWindow.show(
      builder: (context) {
        return MetaData(
          metaData: this,
          behavior: HitTestBehavior.translucent,
          child: widget.builder(context),
        );
      },
      anchor: anchor,
      compositedTransformTarget: compositedTransformTarget,
      offset: widget.offset,
      indicateSize: widget.indicateSize,
      direction: widget.direction,
      margin: widget.margin,
      alignment: widget.alignment,
      link: _link,
      bounds: bounds,
      backgroundColor: widget.backgroundColor,
      borderRadius: widget.borderRadius,
      shadows: widget.shadows,
      side: widget.side,
      barrierDismissible: widget.barrierDismissible,
      barrierColor: widget.barrierColor,
      preferBelow: widget.preferBelow,
      below: widget.below?.call()?._overlayWindow,
      above: widget.above?.call()?._overlayWindow,
      modalBarrier: widget.modalBarrier,
      showWhenUnlinked: widget.showWhenUnlinked,
      useSafeArea: widget.useSafeArea,
    );
  }

  /// 隐藏
  void dismiss() {
    _dismissController();
    _overlayWindow.dismiss();
  }

  void _dismissController() {
    final listener = _listener;
    _listener = null;
    if (listener != null) {
      _controller.removeListener(listener);
      _controller.value = _controller.upperBound;
    }
  }

  void _onDismissed() {
    widget.onDismiss?.call();
    _anchor = null;
    _dismissController();
    _disposePreviousOverlayWindow();
  }

  @override
  Widget build(BuildContext context) {
    var child = widget.child;
    if (widget.link == null) {
      child = CompositedTransformTarget(
        link: _link,
        child: child,
      );
    }
    return child;
  }
}

/// overlay浮层容器控件
class OverlayWindowContainer extends StatefulWidget {
  /// 浮动提示
  const OverlayWindowContainer({
    Key? key,
    required this.child,
    required this.builder,
    this.offset = 0,
    this.indicateSize = defaultIndicateSize,
    this.direction = Axis.vertical,
    this.margin = defaultMargin,
    this.alignment = 0,
    this.link,
    this.onDismiss,
    this.backgroundColor = Colors.white,
    this.borderRadius = defaultBorderRadius,
    this.shadows = defaultShadows,
    this.side = defaultSide,
    this.barrierDismissible = true,
    this.barrierColor,
    this.preferBelow = true,
    this.rootOverlay = true,
    this.below,
    this.above,
    this.modalBarrier = false,
    this.showWhenUnlinked = false,
    this.useSafeArea = false,
  }) : super(key: key);

  /// 需要对齐的child
  final Widget child;

  /// 构建弹窗内容
  final WidgetBuilder builder;

  /// 水平或垂直方向上的偏移量
  final double offset;

  /// 显示的方向，上下结构和左右结构
  final Axis direction;

  /// 三角指示器的大小
  final RSize indicateSize;

  /// 距离屏幕或者[parentRegion]的距离
  final EdgeInsetsGeometry margin;

  /// 对齐方式，[-1,1]，0为居中，-1为最左边，1为最右边
  final double alignment;

  /// 跟踪者
  final LayerLink? link;

  /// 隐藏回调
  final VoidCallback? onDismiss;

  /// 窗口背景颜色
  final Color backgroundColor;

  /// 矩形框的圆角
  final BorderRadiusGeometry borderRadius;

  /// 阴影
  final List<BoxShadow> shadows;

  /// The border outline's color and weight.
  ///
  /// If [side] is [BorderSide.none], which is the default, an outline is not drawn.
  /// Otherwise the outline is centered over the shape's boundary.
  final BorderSide side;

  /// 点击外部区域弹窗是否消失
  final bool barrierDismissible;

  /// 遮罩颜色
  final Color? barrierColor;

  /// 优先显示在末尾
  final bool preferBelow;

  /// 使用跟路由
  final bool rootOverlay;

  /// 显示在below
  final ValueGetter<OverlayWindowAnchorState?>? below;

  /// 显示在above
  final ValueGetter<OverlayWindowAnchorState?>? above;

  /// 是否使用[ModalBarrier]
  final bool modalBarrier;

  /// [CompositedTransformFollower.showWhenUnlinked]
  final bool showWhenUnlinked;

  /// [showDialog.useSafeArea]
  final bool useSafeArea;

  @override
  OverlayWindowContainerState createState() => OverlayWindowContainerState();
}

/// 浮动提示state
class OverlayWindowContainerState extends State<OverlayWindowContainer> {
  final _overlayAnchorKey = GlobalKey<OverlayWindowAnchorState>();

  /// 是否正在显示
  bool get isShowing => _overlayAnchorKey.currentState?.isShowing == true;

  /// 显示
  ///
  /// [anchor]-锚点，在父控件中的位置
  void show(Rect anchor, {TrackBehavior behavior = TrackBehavior.lazy}) {
    final bounds = localToGlobal(context);
    _overlayAnchorKey.currentState?.show(
      anchor: anchor.shift(bounds.topLeft),
      compositedTransformTarget: widget.link == null ? bounds : null,
      bounds: bounds,
      behavior: behavior,
    );
  }

  /// 隐藏
  void dismiss() {
    _overlayAnchorKey.currentState?.dismiss();
  }

  @override
  Widget build(BuildContext context) {
    return OverlayWindowAnchor(
      key: _overlayAnchorKey,
      builder: widget.builder,
      onDismiss: widget.onDismiss,
      margin: widget.margin,
      direction: widget.direction,
      offset: widget.offset,
      indicateSize: widget.indicateSize,
      alignment: widget.alignment,
      link: widget.link,
      backgroundColor: widget.backgroundColor,
      borderRadius: widget.borderRadius,
      shadows: widget.shadows,
      side: widget.side,
      barrierDismissible: widget.barrierDismissible,
      barrierColor: widget.barrierColor,
      preferBelow: widget.preferBelow,
      rootOverlay: widget.rootOverlay,
      below: widget.below,
      above: widget.above,
      modalBarrier: widget.modalBarrier,
      showWhenUnlinked: widget.showWhenUnlinked,
      useSafeArea: widget.useSafeArea,
      child: widget.child,
    );
  }
}

/// 显示Overlay
class OverlayWindow extends LazyChangeNotifier {
  /// 显示Overlay
  OverlayWindow(
    this.context, {
    this.rootOverlay = true,
  });

  /// context
  final BuildContext context;

  /// [Overlay.of]
  final bool rootOverlay;

  final _barrier = LazyOverlayEntryBuilder();
  final _content = LazyOverlayEntryBuilder();

  late final _entries = OverlayEntries(
    overlay: Overlay.of(
      context,
      rootOverlay: rootOverlay,
    ),
    builders: [
      _barrier,
      _content,
    ],
    animationBehavior: AnimationBehavior.preserve,
  );

  void Function(bool immediately)? _dismiss;

  /// [OverlayEntries.mounted]
  bool get mounted => _entries.mounted;

  /// [OverlayEntries.opaque]
  bool get opaque => _entries.opaque;

  /// [OverlayEntries.opaque]
  set opaque(bool value) => _entries.opaque = value;

  /// [OverlayEntries.maintainState]
  bool get maintainState => _entries.maintainState;

  /// [OverlayEntries.maintainState]
  set maintainState(bool value) => _entries.maintainState = value;

  /// [OverlayEntries.markNeedsBuild]
  void markNeedsBuild() => _entries.markNeedsBuild();

  @override
  void didStartListening() {
    _entries.addListener(notifyListeners);
  }

  @override
  void didStopListening() {
    _entries.removeListener(notifyListeners);
  }

  @override
  void notifyListeners() {
    if (mounted) {
      return;
    }
    Timer.run(super.notifyListeners);
  }

  /// 显示
  ///
  /// [anchor]-锚点，在屏幕中的位置
  /// [bounds]-边界，在屏幕中的位置
  /// [compositedTransformTarget]-compositedTransformTarget控件的坐标，默认为anchor的坐标
  void show({
    Rect? anchor,
    Rect? compositedTransformTarget,
    required WidgetBuilder builder,
    double offset = 0,
    RSize indicateSize = RSize.zero,
    Axis direction = Axis.vertical,
    EdgeInsetsGeometry margin = EdgeInsets.zero,
    double alignment = 0,
    LayerLink? link,
    Rect? bounds,
    bool immediately = false,
    Color backgroundColor = Colors.white,
    BorderRadiusGeometry borderRadius = defaultBorderRadius,
    List<BoxShadow> shadows = defaultShadows,
    BorderSide side = defaultSide,
    bool barrierDismissible = true,
    Color? barrierColor,
    bool preferBelow = true,
    OverlayWindow? below,
    OverlayWindow? above,
    bool modalBarrier = false,
    bool showWhenUnlinked = false,
    bool useSafeArea = false,
  }) {
    assert(alignment.abs() <= 1);
    final currentAnchor = anchor ?? localToGlobal(context);
    final route = FlattererRoute<dynamic>(
      builder,
      currentAnchor,
      offset: offset,
      direction: direction,
      indicateSize: indicateSize,
      margin: margin,
      alignment: alignment,
      bounds: bounds,
      backgroundColor: backgroundColor,
      borderRadius: borderRadius,
      shadows: shadows,
      side: side,
      barrierDismissible: barrierDismissible,
      barrierColor: barrierColor,
      preferBelow: preferBelow,
      capturedThemes: InheritedTheme.capture(from: context, to: null),
      useSafeArea: useSafeArea,
    );
    _barrier.builder = (context, animation, secondaryAnimation) {
      return _ModalBarrier(route: route, animation: animation, modalBarrier: modalBarrier);
    };
    _content.builder = (context, animation, secondaryAnimation) {
      Widget child = _OverlayWindowScope(
        overlayWindow: this,
        child: DismissWindowScope(
          dismiss: dismiss,
          child: route.buildPage(context, animation, secondaryAnimation),
        ),
      );
      if (link != null) {
        child = CompositedTransformFollower(
          link: link,
          showWhenUnlinked: showWhenUnlinked,
          offset: -(compositedTransformTarget ?? currentAnchor).topLeft,
          child: child,
        );
      }
      return child;
    };
    _content.transitionBuilder = route.buildTransitions;
    _entries.insert(
      duration: route.transitionDuration,
      below: below?._entries,
      above: above?._entries,
      immediately: immediately,
    );
    _dismiss = (immediately) {
      _entries.remove(
        duration: route.reverseTransitionDuration,
        immediately: immediately,
      );
    };
  }

  /// 隐藏
  void dismiss({bool immediately = false}) {
    _dismiss?.call(immediately);
  }

  @override
  void dispose() {
    _entries.dispose();
    super.dispose();
  }

  /// 获取锚点state
  static OverlayWindow? of(BuildContext context) {
    final widget = context.dependOnInheritedWidgetOfExactType<_OverlayWindowScope>();
    return widget?.overlayWindow;
  }
}

class _ModalBarrier extends StatelessWidget {
  const _ModalBarrier({
    Key? key,
    required this.route,
    required this.animation,
    required this.modalBarrier,
  }) : super(key: key);

  final ModalRoute<Object?> route;
  final Animation<double> animation;
  final bool modalBarrier;

  @override
  Widget build(BuildContext context) {
    final barrierColor = route.barrierColor;
    final barrierDismissible = route.barrierDismissible;
    final barrierLabel = route.barrierLabel;
    final semanticsDismissible = route.semanticsDismissible;
    Widget barrier;
    if (barrierColor != null && barrierColor.alpha != 0) {
      final color = animation.drive(
        ColorTween(
          begin: barrierColor.withOpacity(0),
          end: barrierColor,
        ).chain(CurveTween(curve: route.barrierCurve)),
      );
      barrier = AnimatedModalBarrier(
        color: color,
        dismissible: barrierDismissible,
        semanticsLabel: barrierLabel,
        barrierSemanticsDismissible: semanticsDismissible,
      );
    } else {
      barrier = ModalBarrier(
        dismissible: barrierDismissible,
        semanticsLabel: barrierLabel,
        barrierSemanticsDismissible: semanticsDismissible,
      );
    }
    final status = animation.status;
    barrier = IgnorePointer(
      ignoring: !modalBarrier || status == AnimationStatus.reverse || status == AnimationStatus.dismissed,
      child: barrier,
    );
    if (semanticsDismissible && barrierDismissible) {
      // To be sorted after the _modalScope.
      barrier = Semantics(
        sortKey: const OrdinalSortKey(1),
        child: barrier,
      );
    }
    return barrier;
  }
}

class _OverlayWindowScope extends InheritedWidget {
  const _OverlayWindowScope({
    Key? key,
    required this.overlayWindow,
    required Widget child,
  }) : super(key: key, child: child);

  final OverlayWindow overlayWindow;

  @override
  bool updateShouldNotify(_OverlayWindowScope old) => overlayWindow != old.overlayWindow;
}
